﻿using System.Linq;

using Microsoft.WowAddonStudio.Lua.Model;
using Microsoft.WowAddonStudio.Lua.Parser;

using NUnit.Framework;

namespace Microsoft.WowAddonStudio.Lua.Tests.Model
{
    [TestFixture]
    public class ScopeTest
    {
        [Test]
        public void FindScopeTest()
        {
            var globalScope = new Scope();
            var firstLocalScope = new Scope(globalScope)
                                  {
                                      Location = new LexLocation(1, 1, 10, 10)
                                  };

            var secondLocalScope = new Scope(globalScope)
                                   {
                                       Location = new LexLocation(11, 1, 20, 3)
                                   };

            var level3Scope = new Scope(firstLocalScope)
                              {
                                  Location = new LexLocation(5, 1, 8, 3)
                              };

            // Assert relationships
            Assert.AreEqual(2, globalScope.ChildScopes.Count());
            Assert.AreEqual(1, firstLocalScope.ChildScopes.Count());

            // Assert finding scopes based on location
            Assert.AreSame(globalScope, globalScope.FindScope(25, 1));
            Assert.AreSame(firstLocalScope, globalScope.FindScope(2, 1));
            Assert.AreSame(secondLocalScope, globalScope.FindScope(15, 10));
            Assert.AreSame(level3Scope, globalScope.FindScope(6, 1));
        }

        [Test]
        public void FindNamedScopeTest()
        {
            var globalScope = new Scope();
            var frameLuaScope = new Scope(globalScope)
            {
                Name = "Frame.lua",
                Location = new LexLocation(1, 1, 10, 1)
            };
            var codeLuaScope = new Scope(globalScope)
            {
                Name = "Code.lua",
                Location = new LexLocation(1, 1, 20, 1)
            };

            var frameLuaLocalScope = new Scope(frameLuaScope)
            {
                Location = new LexLocation(1, 1, 5, 3)
            };
            var codeLuaLocalScope = new Scope(codeLuaScope)
            {
                Location = new LexLocation(1, 1, 3, 3)
            };

            Assert.AreSame(frameLuaScope, globalScope.FindScope("Frame.lua"));
            Assert.AreSame(codeLuaScope, globalScope.FindScope("Code.lua"));

            Assert.AreSame(frameLuaScope, globalScope.FindScope("Frame.lua", 8, 1));
            Assert.AreSame(codeLuaScope, globalScope.FindScope("Code.lua", 6, 1));

            Assert.AreSame(frameLuaLocalScope, globalScope.FindScope("Frame.lua", 2, 1));
            Assert.AreSame(codeLuaLocalScope, globalScope.FindScope("Code.lua", 2, 1));

            Assert.AreSame(frameLuaLocalScope, frameLuaScope.FindScope(2, 1));
            Assert.AreSame(codeLuaLocalScope, codeLuaScope.FindScope(2, 1));

            Assert.IsNull(globalScope.FindScope("NonExistent.lua"));
        }

        [Test]
        public void ScopeRelationshipTest()
        {
            var globalScope = new Scope();
            var localScope = new Scope(globalScope);

            // Assert relationships between scopes: Parent, GlobalScope, ChildScopes
            Assert.AreSame(globalScope, localScope.Parent);
            Assert.AreSame(globalScope, localScope.GlobalScope);
            Assert.AreSame(localScope, globalScope.ChildScopes.Single());

            Assert.AreEqual(1, globalScope.Level);
            Assert.AreEqual(2, localScope.Level);
        }

        [Test]
        public void MergeScopeTest()
        {
            // Create two global and three local scopes and merge them into a single new
            // global scope
            var firstGlobalScope = new Scope();
            var secondGlobalScope = new Scope();

            var firstLocalScope = new Scope(firstGlobalScope)
                                  {
                                      Name = "First"
                                  };
            var secondLocalScope = new Scope(secondGlobalScope)
                                   {
                                       Name = "Second"
                                   };
            var thirdLocalScope = new Scope(secondGlobalScope)
                                  {
                                      Name = "Third"
                                  };

            var globalScope = new Scope();

            // Merge the two scopes into the single global scope
            globalScope.Merge(firstGlobalScope);
            globalScope.Merge(secondGlobalScope);

            // Assert the number of child scopes
            Assert.AreEqual(3, globalScope.ChildScopes.Count());

            // Assert the parent's of the local scopes
            Assert.AreSame(globalScope, firstLocalScope.Parent);
            Assert.AreSame(globalScope, secondLocalScope.Parent);
            Assert.AreSame(globalScope, thirdLocalScope.Parent);
        }
    }
}